/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package dnbW.uiW.dbW;

import dnbW.dbW.DatabaseInfo;
import dnbW.dbW.PathExpression2;
import dnbW.dbW.Step;
import dnbW.dbW.DataSource;
import dnbW.uiW.DNBW;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Collection;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;


/**
 * Encapsulate a background task to load the schema for each database.
 * Schema tree is loaded gradually using publish() and process(), not at
 * the end of the background execution. Hence, the result type is void while
 * the intermediate type is schema tree node.
 * @author zhouyong
 */
public class LoadSchemaTask{

    private DatabaseInfo db;
    private Collection<DataSource> dataSourceList;
    //private DefaultTreeModel treeModel;

    public LoadSchemaTask() {
//        this.db = db;
//        this.dataSourceList = dataSourceList;
        //treeModel = (DefaultTreeModel)db.getTreeModel();
    }

    
//    @Override
//    protected Void doInBackground() throws Exception {
//
//        setMessage("Loading Schema..."); // set status message
//
//        // for each data source in the database
//        for(DataSource dataSource : dataSourceList){
//            // build a node representing the whole data source
//            DefaultMutableTreeNode node = dataSource.getTreeNode();
//            // Is the data source node found?
//            if(node == null)
//                // No. Ignore this data source.
//                continue;
//            else{
//                // Yes. Load the schema tree for this data source.
//
//                // First, inform that the schema tree is loading...
//                node.removeAllChildren();
//                node.add(new DefaultMutableTreeNode("Schema is being loaded..."));
//                publish(node);
//                
//                // Load the schema tree and publish the schema tree of this 
//                // data source before processing next data source
//                node = loadSchema(db, dataSource);
//                publish(node);
//            }
//        }
//
//        // Nothing to return. All results have been returned during processing.
//        return null;
//    }

    /**
     * Load schema of a data source of a certain database
     * Returns the updated data source node wrapped in a DefaultMutableTreeNode
     * @param db the context database
     * @param data source
     * @return a DefaultMutableTreeNode containing the data source node
     * @throws java.lang.Exception
     */
    public DefaultMutableTreeNode loadSchema(DatabaseInfo db, DataSource dataSource) throws Exception {
        DefaultMutableTreeNode dataSourceNode = dataSource.getTreeNode();
        if (dataSourceNode == null) {
            return null;
        }

        dataSourceNode.removeAllChildren();

        // get the list of path expressions from database engine
        List<PathExpression2> pathExpressions = db.getEngine().getPaths(dataSource.getName());
        if (pathExpressions.isEmpty()) {
            DefaultMutableTreeNode infoNode =
                    new DefaultMutableTreeNode(DataSource.EMPTY_DATASOURCE_INFO);
            dataSourceNode.add(infoNode);
            return dataSourceNode;
        }

        for (PathExpression2 pathExpression : pathExpressions) {
            DefaultMutableTreeNode current = dataSourceNode;

            Step next = pathExpression.getRootStep();
            while (next != null) {
                String qName = next.getQName();
                Object nodeObject;
                if (next == pathExpression.getLastStep()) {
                    nodeObject = new SchemaTreeLeafNodeObject(pathExpression);
                } else {
                    nodeObject = qName;
                }

                DefaultMutableTreeNode node = getChild(current, qName);
                if (node == null) {
                    node = new DefaultMutableTreeNode(nodeObject);
                    current.add(node);
                }
                current = node;
                next = next.getNext();
            }
        }

        //reorderSchema(dataSourceNode);
        return dataSourceNode;
    }

    /**
     * Finds a child node whose step from the parent node to that node is equal to the given step.
     * @param parent    The parent node
     * @param step      The parent-child step
     * @return          The child node if found; otherwise, null
     */
    private DefaultMutableTreeNode getChild(DefaultMutableTreeNode parent, String step) {
        // For each child of the parent
        for (Enumeration e = parent.children(); e.hasMoreElements();) {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) (e.nextElement());
            // Is the step between the parent and that child the given step?
            if (step.equals(node.getUserObject().toString())) {
                // Yes. Return that child.
                return node;
            }
            // No. Continue finding.
        }
        // No child found. Return empty.
        return null;
    }

    /**
     * Reorder children of a node so that leaf nodes precedes non-leaf nodes
     * @param node  Parent node
     */
    private void reorderSchema(DefaultMutableTreeNode node) {
        // no child, nothing to reorder
        System.out.println("Node 1 = "+node.toString());
        if (node.isLeaf()) {
            return;
        }

        // one child, just ask its child to reorder
        if (node.getChildCount() == 1) {
            System.out.println("Node 2 = "+node.toString());
            reorderSchema((DefaultMutableTreeNode) node.getFirstChild());
        }
        else {
            List<DefaultMutableTreeNode> leafNodes =
                    new ArrayList<DefaultMutableTreeNode>();
            List<DefaultMutableTreeNode> nonLeafNodes =
                    new ArrayList<DefaultMutableTreeNode>();

            for (Enumeration em = node.children(); em.hasMoreElements();) {
                DefaultMutableTreeNode next = (DefaultMutableTreeNode) em.nextElement();
                if (next.isLeaf()) {
                    leafNodes.add(next);
                    System.out.println("Leaf = "+next.toString());
                } else {
                    System.out.println("Non Leaf = "+next.toString());
                    reorderSchema(next);
                    nonLeafNodes.add(next);
                }
            }

            if (nonLeafNodes.size() == 0 || leafNodes.size() == 0) {
                return;
            }

            node.removeAllChildren();
            for (DefaultMutableTreeNode leaf : leafNodes) {
                node.add(leaf);
            }

            for (DefaultMutableTreeNode nonLeaf : nonLeafNodes) {
                node.add(nonLeaf);
            }
        }
    }
}
